/* //device/system/broncho-ril/reference-ril.c
**
** Copyright 2006, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
**     http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/

#include <telephony/ril.h>
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <alloca.h>
#include "atchannel.h"
#include "at_tok.h"
#include "ril_util.h"
#include "ppp_ctrl.h"
#include <getopt.h>
#include "broncho-ril.h"
#include "cutils/properties.h"

#define LOG_TAG "RIL"
#include "broncho_log.h"
static const struct timeval TIMEVAL_NETPOLL = {1,0};
static const struct timeval TIMEVAL_NETPOLL2 = {8,0};


typedef enum _DataCallState
{
	DATA_CALL_STATE_NULL,
	DATA_CALL_STATE_CONNECTING,
	DATA_CALL_STATE_CONNECTED,
	DATA_CALL_STATE_DISCONNECTING,
	DATA_CALL_STATE_NR
}DateCallState;

typedef struct _GprsContext
{
	DateCallState 	state;
	RIL_Token		disconnect_token;
	RIL_Token		setup_token;
	int             poll_times;
	int             poll_ppp;

	PppCtrl*		ppp_ctrl;

	int				usb_power;

	char			apn[32];
}GprsContext;

static const struct timeval TIMEVAL_GPRSPOLL = {5,0};
static const struct timeval TIMEVAL_GPRSFIRSTPOLL = {5, 0};
static const struct timeval TIMEVAL_DISCONNECTPOLL = {2, 0};

static void responseDeactivateDataCall(RIL* thiz, RIL_Token t);
static void unsolDataCallListChanged(RIL* thiz);
static void gprs_on_ppp_state_changed(PppCtrl* ppp_ctrl, PppState state, RIL* thiz);

static void sendNetworkStateChanged(void* param)
{
	RIL* thiz = (RIL* )param;

	RIL_onUnsolicitedResponse (thiz, 
			RIL_UNSOL_RESPONSE_NETWORK_STATE_CHANGED,
			NULL, 0);

}

static GprsContext* getGprsContext(RIL* thiz)
{
	GprsContext* ctx = RIL_GetPrivInfo(thiz, "gprs_ctx");
	
	if(ctx == NULL)
	{
		ctx = calloc(1, sizeof(GprsContext));
		ctx->ppp_ctrl = ppp_ctrl_create((OnPppStateChangedFunc)gprs_on_ppp_state_changed, thiz);
		ppp_ctrl_stop_pppd(ctx->ppp_ctrl);
		RIL_SetPrivInfo(thiz, ctx, "gprs_ctx");
	}

	return ctx;
}

static void gprs_set_usb_power(RIL* thiz, int on)
{
	GprsContext* ctx = getGprsContext(thiz);
	
	if(ctx->usb_power != on)
	{
		ctx->usb_power = on;
		RIL_PowerUsbDevice(thiz, on);
	}

	return;
}

static void gprs_enter_state(RIL* thiz, DateCallState state) 
{
	GprsContext* ctx = getGprsContext(thiz);

	ctx->state = state;
	if(state == DATA_CALL_STATE_NULL)
	{
		gprs_set_usb_power(thiz, 0);
	}
	else
	{
		gprs_set_usb_power(thiz, 1);
	}

	return;
}

static void handle_ppp_state_idle(RIL* thiz)
{
	GprsContext* ctx = getGprsContext(thiz);
	if(ctx->state == DATA_CALL_STATE_CONNECTING)
	{
		if(ctx->setup_token != NULL) 
		{
			if(!RIL_ExistCalls(thiz))
			{
				ril_command_exec_for_result(thiz, "ATH");
			}
			ppp_ctrl_stop_pppd(ctx->ppp_ctrl);
			RIL_onRequestComplete(thiz, ctx->setup_token, RIL_E_GENERIC_FAILURE, NULL, 0);
		}
		ctx->setup_token = NULL;
		gprs_enter_state(thiz, DATA_CALL_STATE_NULL);
		unsolDataCallListChanged(thiz);
	} 
	else if(ctx->state == DATA_CALL_STATE_CONNECTED) 
	{
		gprs_enter_state(thiz, DATA_CALL_STATE_NULL);
		unsolDataCallListChanged(thiz);
	} 
	else if(ctx->state == DATA_CALL_STATE_DISCONNECTING)
	{
		if(ctx->disconnect_token != NULL)
		{
			responseDeactivateDataCall(thiz, ctx->disconnect_token);
			ctx->disconnect_token = NULL;
		}
		gprs_enter_state(thiz, DATA_CALL_STATE_NULL);
	}

	return;
}

static void handle_ppp_state_up(RIL* thiz)
{
	GprsContext* ctx = getGprsContext(thiz);

	if(ctx->state == DATA_CALL_STATE_CONNECTING && ctx->setup_token != NULL)
	{
		char* responseStr[3] = {"0", "ppp0"};
		char addr[PROPERTY_VALUE_MAX] = {0};

		property_get("net.ppp0.local-ip", addr, "");
		if(strlen(addr) == 0) 
		{
			handle_ppp_state_idle(thiz);
		}
		else
		{
			responseStr[2] = addr;	
			RIL_onRequestComplete(thiz, ctx->setup_token, RIL_E_SUCCESS, responseStr, sizeof(responseStr));
			ctx->setup_token = NULL;
			gprs_enter_state(thiz, DATA_CALL_STATE_CONNECTED);
		}
	}

	return;
}

static void handle_ppp_state_down(RIL* thiz)
{
	GprsContext* ctx = getGprsContext(thiz);
	if(ctx->state == DATA_CALL_STATE_CONNECTED)
	{
		gprs_enter_state(thiz, DATA_CALL_STATE_NULL);
		ppp_ctrl_stop_pppd(ctx->ppp_ctrl);
		unsolDataCallListChanged(thiz);
	}

	return;
}

static void gprs_on_ppp_state_changed(PppCtrl* ppp_ctrl, PppState state, RIL* thiz)
{
	GprsContext* ctx = getGprsContext(thiz);

	ALOGD("%s: state=%d, data_state=%d, setup_token=%p", __func__,
				state, ctx->state, ctx->setup_token);
	switch(state)
	{
	case PPP_STATE_IDLE:
		handle_ppp_state_idle(thiz);
		break;

	case PPP_STATE_UP:
		handle_ppp_state_up(thiz);
		break;

	case PPP_STATE_DOWN:
		handle_ppp_state_down(thiz);
		break;

	default:
		ALOGD("%s: error state", __func__);
		break;
	}
}

static void startConnectPpp(RIL* thiz, const char* user, const char* pwd, int authtype, RIL_Token t)
{
	GprsContext* ctx = getGprsContext(thiz);

	if(ctx->state == DATA_CALL_STATE_DISCONNECTING && ctx->disconnect_token != NULL)
	{
		responseDeactivateDataCall(thiz, ctx->disconnect_token);
		ctx->disconnect_token = NULL;
	}

	/*start pppd_gprs service*/
	gprs_enter_state(thiz, DATA_CALL_STATE_CONNECTING);
	ctx->setup_token = t;
	user = (user == NULL) ? "ctnet@mycdma.cn" : user;
	pwd = (pwd == NULL) ? "vnet.mobi" : pwd;
	ppp_ctrl_start_pppd(ctx->ppp_ctrl,
			RIL_GetDataChannelDeviceName(thiz), user, pwd, authtype);

	return;
}

static void responseDeactivateDataCall(RIL* thiz, RIL_Token t)
{
	ALOGD("responseDeactivateDataCall: \n");
	
	if(!RIL_ExistCalls(thiz))
	{
		if(!ril_command_exec_for_result(thiz, "ATH"))
		{
			ril_command_exec_for_result(thiz, "ATH");
		}
	}

	if(t != NULL)
	{
		RIL_onRequestComplete(thiz, t, RIL_E_SUCCESS, NULL, 0);
	}
}

static void disconnectPpp(RIL* thiz, RIL_Token t)
{
	GprsContext* ctx = getGprsContext(thiz);
	
	if(ctx->state != DATA_CALL_STATE_NULL)
	{
		/*stop pppd_gprs service*/
		if(ctx->state == DATA_CALL_STATE_DISCONNECTING && ctx->disconnect_token != NULL)
		{
			ALOGD("respDissconnect: t=%p", ctx->disconnect_token);
			RIL_onRequestComplete(thiz, ctx->disconnect_token, RIL_E_GENERIC_FAILURE, NULL, 0);
			ctx->disconnect_token = NULL;
		}
		gprs_enter_state(thiz, DATA_CALL_STATE_DISCONNECTING);
		ctx->disconnect_token= t;
		ppp_ctrl_stop_pppd(ctx->ppp_ctrl);
	}
	else
	{
		responseDeactivateDataCall(thiz, t);
	}

	return;
}

static void requestSetupDataCall(RIL* thiz, void *data, size_t datalen, RIL_Token t)
{
	int			success = 0;
	ATChannel* 	data_channel = NULL;
	char		cmd[128] = {0};
	char*		apn = NULL;
	GprsContext* ctx = getGprsContext(thiz);
	int			authtype = 0;

	if(ctx->state != DATA_CALL_STATE_NULL)
	{
		disconnectPpp(thiz, NULL);
		sleep(10);
	}

	ALOGD("requestSetupDataCall: apn=%s, user=%s, pwd=%s\n", 
		((char**)data)[2], ((char**)data)[3], ((char**)data)[4]);
	authtype = atoi(((char**)data)[5]);
	gprs_set_usb_power(thiz, 1);
	RIL_OpenDataChannel(thiz);
	do
	{
		if ((data_channel = RIL_GetDataChannel(thiz)) == NULL)
		{
			RIL_ResetUsbDevice(thiz);
			break;
		}
		
		ril_command_exec_for_result(thiz, "ATH");
		if(at_send_command(data_channel, "AT", DEFAULT_AT_TIMEOUT_MSEC, NULL) < 0)
		{
			ALOGD("GPRS Attention fail");
			break;
		}

		if(!at_command_exec_for_result(data_channel, "ATD#777", 50000))
		{
			break;
		}
		success = 1;
	}while(0);

	if(success)
	{
		startConnectPpp(thiz, ((char**)data)[3], ((char**)data)[4], authtype, t);
	}
	else
	{
		gprs_set_usb_power(thiz, 0);
		RIL_onRequestComplete(thiz, t, RIL_E_GENERIC_FAILURE, NULL, 0);
	}
	RIL_CloseDataChannel(thiz);
	
	//as the modem will ignore CNETS while after ATD#777 and before, we need poll it after CONNECT.
	RIL_requestTimedCallback(thiz, sendNetworkStateChanged, thiz, &TIMEVAL_NETPOLL);
	RIL_requestTimedCallback(thiz, sendNetworkStateChanged, thiz, &TIMEVAL_NETPOLL2);
	
	return;
}

static void requestDeactivateDataCall(RIL* thiz, void *data, size_t datalen, RIL_Token t)
{
	disconnectPpp(thiz, t);
	
	return;
}
static int convertRegistrationState(int state)
{
	if(state != 0)
	{
		return 1; //registed
	}
	else
	{
		return 0;//not registed.
	}
}
//convert to telephony format.
static int convertNetworkType(int type)
{
	switch(type)
	{
		case 0:
			return 0;
		case 1:
		 	return 0;
		case 2: //CDMA
			return 7; //EVDO_0 
		case 3: //GSM/GPRS
			return 1;
		case 4://HDR
			return 7;
		case 5://WCDMA
			return 10;
		case 6://GPS
			return 0;
		case 7://GSM/WCDMA
			return 10;
		case 8://CDMA/HDR HYBRID
			return 8;
		default:
			return 0;//unknown
	}
}

//wupeng add begin.
static void requestGprsRegistrationState(RIL* thiz, void *data, size_t datalen, RIL_Token t)
{
	int err = 0;
	ATResponse* p_resp = NULL;
	char* responseStr[4] = {0};
	int state = 0;
	int network_type = 0;

	if(ril_command_exec_singleline_for_result(thiz, "AT^SYSINFO?", "^SYSINFO", &p_resp))
	{
		int skip = 0;
		char* line = NULL;

		line = p_resp->p_intermediates->line;
		at_line_scanf_int_int_int_int(line, &state, &skip, &skip, &network_type);
	}

	if(err < 0)
	{
		RIL_onRequestComplete(thiz, t, RIL_E_GENERIC_FAILURE, NULL, 0);
	}
	else
	{
		asprintf(&responseStr[0], "%d", convertRegistrationState(state));
		asprintf(&responseStr[3], "%d", convertNetworkType(network_type));
		RIL_onRequestComplete(thiz, t, RIL_E_SUCCESS, responseStr, sizeof(responseStr));
		free(responseStr[0]);
		free(responseStr[3]);
	}

	at_response_free(p_resp);

	return;
}
//wupeng add end.

static void requestLastDataCallFailCause(RIL* thiz, void* data, int datalen, RIL_Token t)
{
	int failCase = CALL_FAIL_NORMAL;

	ALOGD("requestLastDataCallFailCause \n");
	RIL_onRequestComplete(thiz, t, RIL_E_SUCCESS, &failCase, sizeof(failCase));

	return;
}

static void getDataCallResponse(RIL* thiz, RIL_Data_Call_Response_v4* resp)
{
	GprsContext* ctx = getGprsContext(thiz);
	char addr[PROPERTY_VALUE_MAX] = {0};

	property_get("net.ppp0.local-ip", addr, "");
	resp->cid = 0;
	resp->type = "IP";
	resp->apn = ctx->apn;
	resp->address = addr;
	if(ctx->state == DATA_CALL_STATE_NULL)
	{
		resp->active = 0;
	}
	else
	{
		resp->active = 2;
	}

	return;
}

static void unsolDataCallListChanged(RIL* thiz)
{
	RIL_Data_Call_Response_v4 responses;

	memset(&responses, 0 , sizeof(RIL_Data_Call_Response_v4));
	getDataCallResponse(thiz, &responses);
	RIL_onUnsolicitedResponse(thiz, RIL_UNSOL_DATA_CALL_LIST_CHANGED, 
		&responses, sizeof(RIL_Data_Call_Response_v4));

	return;
}

static void requestDataCallList(RIL* thiz, void* data, int datalen, RIL_Token t)
{
	RIL_Data_Call_Response_v4 responses;
	
	memset(&responses, 0 , sizeof(RIL_Data_Call_Response_v4));
	getDataCallResponse(thiz, &responses);
	RIL_onRequestComplete(thiz, t, RIL_E_SUCCESS, &responses,
							sizeof(RIL_Data_Call_Response_v4));

	return;
}

int RIL_initialize_gprs (RIL* thiz)
{
	GprsContext* ctx = getGprsContext(thiz);
	gprs_set_usb_power(thiz, 1);
	at_command_exec_for_result(RIL_GetDataChannel(thiz), "ATE0", DEFAULT_AT_TIMEOUT_MSEC);
	gprs_set_usb_power(thiz, 0);

	ril_command_exec_full_for_result(thiz, "AT+CTA=1", 25000);

	return 1;
}

int RIL_onUnsolicited_gprs (RIL* thiz, const char* s)
{
	int ret = 0;

	return ret;
}


int RIL_onRequest_gprs (RIL* thiz, int request, void *data, size_t datalen, RIL_Token t)
{
	switch (request)
	{
	case RIL_REQUEST_SETUP_DATA_CALL:
		requestSetupDataCall(thiz, data, datalen, t);
		break;

	case RIL_REQUEST_DEACTIVATE_DATA_CALL:
		requestDeactivateDataCall(thiz, data, datalen, t);
		break;

	case RIL_REQUEST_LAST_DATA_CALL_FAIL_CAUSE:
		requestLastDataCallFailCause(thiz, data, datalen, t);
		break;

	case RIL_REQUEST_DATA_CALL_LIST:
		requestDataCallList(thiz, data, datalen, t);
		break;

	case RIL_REQUEST_GPRS_REGISTRATION_STATE:
		requestGprsRegistrationState(thiz, data, datalen, t);
		break;

	default:
		RIL_onRequestComplete(thiz, t, RIL_E_REQUEST_NOT_SUPPORTED, NULL, 0);
		break;

	}

	return 1;

}

